package jxtend.processor.eclipse.visitor.interceptor;

import jxtend.processor.eclipse.utils.ASTNodeUtils;
import jxtend.processor.eclipse.utils.EclipseProcessorUtils;

import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;

@SuppressWarnings("restriction")
public class FixNullArgumentTypeOfAnonymousClassConstructor extends ASTNodeVisitorInterceptorAdapter {

	@Override
	public boolean interceptVisit(ASTNodeVisitorInterceptorContext interceptorChain, ASTNode node, Scope scope) {
		if (node instanceof Argument && ((Argument) node).type == null // && ((Argument) node).binding.type != null
				&& interceptorChain.getParentNode(0) instanceof ConstructorDeclaration
				&& (((ConstructorDeclaration) interceptorChain.getParentNode(0)).bits
						& ASTNode.IsDefaultConstructor) != 0
				&& interceptorChain.getParentNode(1) instanceof TypeDeclaration
				&& interceptorChain.getParentNode(2) instanceof QualifiedAllocationExpression) {

			ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) interceptorChain.getParentNode(0);
			constructorDeclaration.bindArguments();

			Argument argument = (Argument) node;
			long pos = EclipseProcessorUtils.encodePos(node.sourceStart, node.sourceEnd);

			if (argument.binding.type instanceof ArrayBinding) {
				ArrayBinding arrayBinding = (ArrayBinding) ((Argument) node).binding.type;

				if (arrayBinding.leafComponentType instanceof BaseTypeBinding) {
					BaseTypeBinding baseTypeBinding = (BaseTypeBinding) arrayBinding.leafComponentType;
					
					((Argument) node).type = new SingleTypeReference(baseTypeBinding.simpleName, pos);
				} else {
					assert arrayBinding.leafComponentType instanceof ReferenceBinding;
					char[][] compoundName = ((ReferenceBinding) arrayBinding.leafComponentType).compoundName; 

					((Argument) node).type = new QualifiedTypeReference(
								compoundName, ASTNodeUtils.encodePosArray(compoundName.length, pos));
				}
			} else if (argument.binding.type instanceof BaseTypeBinding) {
				BaseTypeBinding baseTypeBinding = (BaseTypeBinding) argument.binding.type;

				((Argument) node).type = new SingleTypeReference(baseTypeBinding.simpleName, pos);
			} else {
				assert argument.binding.type instanceof ReferenceBinding;

				char[][] compoundName = ((ReferenceBinding) argument.binding.type).compoundName; 

				((Argument) node).type = new QualifiedTypeReference(
							compoundName, ASTNodeUtils.encodePosArray(compoundName.length, pos));				
			}
		}

		return super.interceptVisit(interceptorChain, node, scope);
	}

}
